package baoyunfs.deviceid;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Base64;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * Created by guolin on 2017/3/31.
 */

public class UUDidStore {
    private final static String DID_SP_NAME = "Did";
    private final static String KEY_DID = "key_did";
    private final static String DID_CR_NAME = "f020709f4cf61dc452d56ba7e59263d2";
    private final static String DID_SDCARD_PATH = ".did/did.conf";

    private final static String ALGORITHM = "DES";
    public static final String TRANSFORMATION = "DES/CBC/PKCS5Padding";
    private final static byte[] DES_IV = { 1, 2, 3, 4, 5, 6, 7, 8 };
    private final static byte[] DES_KEY = "30aa0ddd".getBytes();
    private final static String ID_PREFIX = "chl_";

    public static String loadDid(Context context,boolean sdCardUsable) {
        return load(context, DID_SP_NAME, KEY_DID, DID_CR_NAME, DID_SDCARD_PATH,sdCardUsable);
    }

    private static String load(Context context, String spName, String key, String crName, String sdcardPath,boolean sdCardUsable) {
        SharedPreferences sp = context.getSharedPreferences(spName, Context.MODE_PRIVATE);
        String id = decryptID(sp.getString(key, null));
        if (!TextUtils.isEmpty(id)) return id;

        // 防止抛出异常
        try {
            id = decryptID(
                Settings.System.getString(context.getContentResolver(), crName));
            if (!TextUtils.isEmpty(id)) return id;
        } catch (Throwable e) {
            e.printStackTrace();
        }

        if(sdCardUsable){
            id = decryptID(readFromSdcard(sdcardPath));
        }
        if (!TextUtils.isEmpty(id)) return id;

        return null;
    }

    public static void saveDid(Context context, String did,boolean sdCardUsable) {
        save(context, DID_SP_NAME, KEY_DID, DID_CR_NAME, DID_SDCARD_PATH, did,sdCardUsable);
    }

    private static void save(Context context, String spName, String key, String crName, String sdcardPath, String id,boolean sdCardUsable) {
        String encryptedID = encryptID(id);
        SharedPreferences sp = context.getSharedPreferences(spName, Context.MODE_PRIVATE);
        String data = sp.getString(key, null);
        if(TextUtils.isEmpty(data) || !data.equals(encryptedID)) {
            sp.edit().putString(key, encryptedID).apply();
        }

        // Android 7.0需要动态申请android.permission.WRITE_SETTINGS权限
        // 若未申请，会抛出SecurityException
        // 我们不去申请，以防用户认为我们是恶意软件
        try {
            data = Settings.System.getString(context.getContentResolver(), crName);
            if(TextUtils.isEmpty(data) || !data.equals(encryptedID)) {
                Settings.System.putString(context.getContentResolver(), crName, encryptedID);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }

        if(sdCardUsable){
            data = readFromSdcard(sdcardPath);
            if(TextUtils.isEmpty(data) || !data.equals(encryptedID)) {
                writeIDToSdcard(sdcardPath, encryptedID);
            }
        }
    }

    private static String readFromSdcard(String path) {
        String state = Environment.getExternalStorageState();
        if (!state.equals(Environment.MEDIA_MOUNTED)) return null;

        File sdcard = Environment.getExternalStorageDirectory();
        File dataFile = new File(sdcard, path);
        if (!dataFile.exists()) return null;

        FileInputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(dataFile);
            byte[] buffer = new byte[1024];
            outputStream = new ByteArrayOutputStream();
            int readBytes = inputStream.read(buffer);
            if(readBytes > 0) {
                outputStream.write(buffer, 0, readBytes);
            }

            return outputStream.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                }
            }
        }

        return null;
    }

    private static void writeIDToSdcard(String path, String data) {
        String state = Environment.getExternalStorageState();
        if (!state.equals(Environment.MEDIA_MOUNTED)) return;

        File sdcard = Environment.getExternalStorageDirectory();
        File dataFile = new File(sdcard, path);
        dataFile.getParentFile().mkdirs();

        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(dataFile);
            outputStream.write(data.getBytes());
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    private static String encryptID(String id) {
        id = ID_PREFIX + id;
        byte[] encryptedBytes = id.getBytes();
        try {
            IvParameterSpec zeroIv = new IvParameterSpec(DES_IV);
            SecretKeySpec key = new SecretKeySpec(DES_KEY, ALGORITHM);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
            encryptedBytes = cipher.doFinal(encryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP);
    }

    private static String decryptID(String data) {
        if (TextUtils.isEmpty(data)) return null;

        byte[] decryptedBytes = Base64.decode(data, Base64.NO_WRAP);
        try {
            IvParameterSpec zeroIv = new IvParameterSpec(DES_IV);
            SecretKeySpec key = new SecretKeySpec(DES_KEY, ALGORITHM);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, key, zeroIv);
            decryptedBytes = cipher.doFinal(decryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String id = new String(decryptedBytes);
        if (!TextUtils.isEmpty(id) && id.startsWith(ID_PREFIX)) {
            return id.substring(ID_PREFIX.length());
        }

        return null;
    }
}
